<template>
  <div class="fm-making-form">
    <div class="fm-container">
      <div class="fm-aside">
        <div class="fm-widget-list">
          <div class="fm-widget-cate">
            <div class="fm-widget-cate-title">基础组件</div>
            <draggable
              class="fm-widget-items"
              :list="basicComponents"
              :group="{ name: 'widget', pull: 'clone', put: false }"
              :clone="handleClone"
              :sort="false"
              item-key="type"
            >
              <template #item="{ element }">
                <div class="fm-widget-item">
                  <i :class="element.icon"></i>
                  <span>{{ element.label }}</span>
                </div>
              </template>
            </draggable>
          </div>
          <div class="fm-widget-cate">
            <div class="fm-widget-cate-title">布局组件</div>
            <draggable
              class="fm-widget-items"
              :list="layoutComponents"
              :group="{ name: 'widget', pull: 'clone', put: false }"
              :clone="handleClone"
              :sort="false"
              item-key="type"
            >
              <template #item="{ element }">
                <div class="fm-widget-item">
                  <i :class="element.icon"></i>
                  <span>{{ element.label }}</span>
                </div>
              </template>
            </draggable>
          </div>
        </div>
      </div>

      <div class="fm-main">
        <div
          class="fm-actions"
          v-if="preview || generateJson || generateCode || clearable"
        >
          <el-button v-if="preview" @click="handlePreview" type="primary"
            >预览</el-button
          >
          <el-button v-if="generateJson" @click="handleGenerateJson"
            >导出JSON</el-button
          >
          <el-button v-if="generateCode" @click="handleGenerateCode"
            >导出代码</el-button
          >
          <el-button v-if="clearable" @click="handleClear" type="danger"
            >清空</el-button
          >
        </div>
        <el-form
          :label-width="formConfig.labelWidth"
          :label-position="formConfig.labelPosition"
          :size="formConfig.size"
          class="fm-design-area"
        >
          <draggable
            class="fm-widget-form"
            :list="widgetList"
            group="widget"
            item-key="key"
            :animation="200"
            @add="handleAdd"
          >
            <template #item="{ element, index }">
              <div
                class="fm-widget-view"
                :class="{
                  active: selectWidget && selectWidget.key === element.key,
                }"
                @click.stop="handleSelectWidget(element)"
              >
                <widget-form-item :element="element" :config="formConfig" />
                <div
                  class="fm-widget-view-action"
                  v-if="selectWidget && selectWidget.key === element.key"
                >
                  <i
                    class="el-icon-copy-document"
                    @click.stop="handleCopy(index)"
                  ></i>
                  <i
                    class="el-icon-delete"
                    @click.stop="handleDelete(index)"
                  ></i>
                </div>
                <div class="fm-widget-view-drag">
                  <i class="el-icon-rank"></i>
                </div>
              </div>
            </template>
          </draggable>
        </el-form>
      </div>

      <div class="fm-config">
        <el-tabs v-model="configTab">
          <el-tab-pane label="字段属性" name="widget">
            <widget-config v-if="selectWidget" :data="selectWidget" />
          </el-tab-pane>
          <el-tab-pane label="表单属性" name="form">
            <form-config :data="formConfig" />
          </el-tab-pane>
        </el-tabs>
      </div>
    </div>

    <el-dialog v-model="previewVisible" title="表单预览" width="800px">
      <generate-form
        v-if="previewVisible"
        :data="previewData"
        ref="generateForm"
      />
      <template #footer>
        <el-button @click="previewVisible = false">取消</el-button>
        <el-button type="primary" @click="handleGetData">获取数据</el-button>
      </template>
    </el-dialog>

    <el-dialog v-model="jsonVisible" title="JSON数据" width="800px">
      <code-editor
        v-model="jsonData"
        :style="{ height: '500px' }"
        :autofocus="true"
        :indent-with-tab="true"
        :tab-size="2"
        :extensions="extensions"
      />
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, reactive, shallowRef } from "vue";
import draggable from "vuedraggable";
import { ElMessage, ElMessageBox } from "element-plus";
import CodeEditor from "../CodeEditor.vue";
import { javascript } from "@codemirror/lang-javascript";
import { oneDark } from "@codemirror/theme-one-dark";
import { basicComponents, layoutComponents } from "../../config/widget";
import WidgetFormItem from "./WidgetFormItem.vue";
import WidgetConfig from "./WidgetConfig.vue";
import FormConfig from "./FormConfig.vue";
import GenerateForm from "../GenerateForm/index.vue";

const extensions = shallowRef([javascript(), oneDark]);

const props = defineProps({
  preview: Boolean,
  generateJson: Boolean,
  generateCode: Boolean,
  clearable: Boolean,
});

const widgetList = ref([]);
const selectWidget = ref(null);
const configTab = ref("widget");
const previewVisible = ref(false);
const jsonVisible = ref(false);
const previewData = ref({});
const jsonData = ref("");
const generateForm = ref(null);

const formConfig = reactive({
  labelWidth: "100px",
  labelPosition: "right",
  size: "default",
});

const handleClone = (element) => {
  const cloned = JSON.parse(JSON.stringify(element));
  cloned.key = `${element.type}_${Date.now()}`;
  cloned.model = cloned.key;
  return cloned;
};

const handleAdd = () => {
  if (widgetList.value.length > 0) {
    selectWidget.value = widgetList.value[widgetList.value.length - 1];
  }
};

const handleSelectWidget = (element) => {
  selectWidget.value = element;
  configTab.value = "widget";
};

const handleCopy = (index) => {
  const widget = JSON.parse(JSON.stringify(widgetList.value[index]));
  widget.key = `${widget.type}_${Date.now()}`;
  widget.model = widget.key;
  widgetList.value.splice(index + 1, 0, widget);
  ElMessage.success("复制成功");
};

const handleDelete = (index) => {
  ElMessageBox.confirm("确定删除该字段吗?", "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    widgetList.value.splice(index, 1);
    selectWidget.value = null;
  });
};

const handleClear = () => {
  ElMessageBox.confirm("确定清空所有字段吗?", "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    widgetList.value = [];
    selectWidget.value = null;
  });
};

const handlePreview = () => {
  previewData.value = {
    list: widgetList.value,
    config: formConfig,
  };
  previewVisible.value = true;
};

const handleGetData = () => {
  generateForm.value
    .getData()
    .then((data) => {
      console.log("表单数据:", data);
      ElMessage.success("数据已打印到控制台");
    })
    .catch(() => {
      ElMessage.error("表单验证失败");
    });
};

const handleGenerateJson = () => {
  jsonData.value = JSON.stringify(
    {
      list: widgetList.value,
      config: formConfig,
    },
    null,
    2
  );
  jsonVisible.value = true;
};

const handleGenerateCode = () => {
  const code = `<template>
  <generate-form :data="formData" ref="generateForm" />
</template>

<script setup>
import { ref } from 'vue'
import { GenerateForm } from 'vue3-form-making'

const formData = ref(${JSON.stringify(
    { list: widgetList.value, config: formConfig },
    null,
    2
  )})
const generateForm = ref(null)

const getData = () => {
  generateForm.value.getData().then(data => {
    console.log(data)
  })
}
<\/script>`;
  jsonData.value = code;
  jsonVisible.value = true;
};

const getJson = () => {
  return {
    list: widgetList.value,
    config: formConfig,
  };
};

const setJson = (json) => {
  if (json.list) {
    widgetList.value = json.list;
  }
  if (json.config) {
    Object.assign(formConfig, json.config);
  }
};

defineExpose({
  getJson,
  setJson,
});
</script>

<style lang="scss" scoped>
.fm-making-form {
  height: 100%;
  width: 100%;

  .fm-container {
    display: flex;
    height: 100%;
    border: 1px solid #ddd;
  }

  .fm-aside {
    width: 260px;
    background: #fff;
    border-right: 1px solid #ddd;
    overflow-y: auto;
  }

  .fm-widget-cate-title {
    padding: 10px;
    font-weight: bold;
    border-bottom: 1px solid #eee;
  }

  .fm-widget-items {
    padding: 10px;
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
  }

  .fm-widget-item {
    width: calc(50% - 5px);
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
    cursor: move;
    text-align: center;
    transition: all 0.3s;

    &:hover {
      border-color: #409eff;
      color: #409eff;
    }

    i {
      display: block;
      font-size: 24px;
      margin-bottom: 5px;
    }

    span {
      font-size: 12px;
    }
  }

  .fm-main {
    flex: 1;
    display: flex;
    flex-direction: column;
    background: #f5f5f5;
    overflow: hidden;
  }

  .fm-actions {
    padding: 10px;
    background: #fff;
    border-bottom: 1px solid #ddd;
  }

  .fm-design-area {
    flex: 1;
    overflow-y: auto;
    padding: 20px;
  }

  .fm-widget-form {
    min-height: 400px;
  }

  .fm-widget-view {
    position: relative;
    padding: 10px;
    margin-bottom: 10px;
    background: #fff;
    border: 1px dashed transparent;
    cursor: pointer;

    &:hover,
    &.active {
      border-color: #409eff;
    }

    &.active {
      .fm-widget-view-action,
      .fm-widget-view-drag {
        display: block;
      }
    }
  }

  .fm-widget-view-action {
    display: none;
    position: absolute;
    top: 0;
    right: 0;
    background: #409eff;
    color: #fff;
    padding: 5px;
    z-index: 10;

    i {
      margin: 0 5px;
      cursor: pointer;

      &:hover {
        opacity: 0.8;
      }
    }
  }

  .fm-widget-view-drag {
    display: none;
    position: absolute;
    top: 0;
    left: 0;
    background: #409eff;
    color: #fff;
    padding: 5px;
    cursor: move;
  }

  .fm-config {
    width: 300px;
    background: #fff;
    border-left: 1px solid #ddd;
    overflow-y: auto;

    :deep(.el-tabs) {
      height: 100%;
      display: flex;
      flex-direction: column;

      .el-tabs__header {
        margin: 0;
      }

      .el-tabs__content {
        flex: 1;
        overflow-y: auto;
        padding: 10px;
      }
    }
  }
}
</style>
